#include "stream_pusher.h"

namespace stream {

RKStreamPusher::RKStreamPusher(StreamPusherInfo* pusher_ifo, LogInfo* log_info): 
  BasePusher(pusher_ifo, log_info) {
    encode_put_frame_flag = 0;
}

RKStreamPusher::~RKStreamPusher() {

}

void RKStreamPusher::release_ffmpeg() {
  // TODO:详细研究释放规则
  // avformat_close_input(&pfmt_ctx);
  // avcodec_close(out_codec_ctx);
    // avcodec_free_context(out_codec_ctx);
  avformat_free_context(pfmt_ctx);
  pfmt_ctx = nullptr;
}

// void RKStreamPusher::release_encoder() {
//   int ret = 0;
//   ret = HB_VENC_StopRecvFrame(pusher_ifo->vEncChn);
//   if (ret != 0) { printf("HB_VENC_StopRecvFrame failed\n"); }

//   ret = HB_VENC_DestroyChn(pusher_ifo->vEncChn);
//   if (ret != 0) { printf("HB_VENC_DestroyChn failed\n"); }

// }

void RKStreamPusher::deinit() {

    p->mpi->reset(p->ctx);

	if (p->ctx) {
		mpp_destroy(p->ctx);
		p->ctx = NULL;
	}

	if (p->cfg) {
		mpp_enc_cfg_deinit(p->cfg);
		p->cfg = NULL;
	}

	if (p->frm_buf) {
		mpp_buffer_put(p->frm_buf);
		p->frm_buf = NULL;
	}

	// if (p->osd_idx_buf) {
	// 	mpp_buffer_put(p->osd_idx_buf);
	// 	p->osd_idx_buf = NULL;
	// }
  if(sps_header.data)  //释放掉手动申请的内存
  {
    free(sps_header.data);
  }
	enc_ctx_deinit(&p);
}

MPP_RET RKStreamPusher::enc_ctx_deinit(MpiEncData **data) {
	MpiEncData *p = NULL;

	if (!data) {
		printf("invalid input data %p\n", data);
		return MPP_ERR_NULL_PTR;
	}

	p = *data;
	if (p) {

		MPP_FREE(p);
		*data = NULL;
	}

	return MPP_OK;
}

int RKStreamPusher::init_pusher_params() {
  int ret = 0;
  char errbuf[64];

  av_register_all();       // Register all codecs and formats so that they can be used.
  ret = avformat_network_init();
  if ((ret = avformat_alloc_output_context2(&pfmt_ctx, nullptr, pusher_ifo->formatName.c_str(), nullptr)) < 0) {
    spdlog::get("logger")->info("ERROR. RKStreamPusher::init_params. Could not allocate output format context!");
    return -103;
  }

  if (!(pfmt_ctx->oformat->flags & AVFMT_NOFILE)) {
    if ((ret = avio_open2(&pfmt_ctx->pb, pusher_ifo->streamAddress.c_str(), AVIO_FLAG_WRITE, nullptr, nullptr)) < 0) {
    spdlog::get("logger")->info("ERROR. RKStreamPusher::init_params. Could not open output IO context!");
    return -102;
    }
  }

  AVCodecID encoder_type = AV_CODEC_ID_NONE;
  if (pusher_ifo->enType == AV_CODEC_ID_H264) { 
    encode_type = MPP_VIDEO_CodingAVC;
    encoder_type = AV_CODEC_ID_H264;
  }
  else if (pusher_ifo->enType == AV_CODEC_ID_HEVC) { 
    encode_type = MPP_VIDEO_CodingHEVC; 
    encoder_type = AV_CODEC_ID_HEVC;
  }

  if ((ret = init_venc_params()) != 0) {
    printf("[ERROR] init_venc_params %d error\n", pusher_ifo->vEncChn);
    return ret;
  }
  out_codec = avcodec_find_encoder(encoder_type);
  out_stream = avformat_new_stream(pfmt_ctx, out_codec);
  out_codec_ctx = avcodec_alloc_context3(out_codec);

  out_codec_ctx->codec_tag = 0;
  out_codec_ctx->codec_id = out_codec->id;
  out_codec_ctx->codec_type = AVMEDIA_TYPE_VIDEO;
  out_codec_ctx->width = pusher_ifo->dstWidth;
  out_codec_ctx->height = pusher_ifo->dstHeight;
  out_codec_ctx->gop_size = pusher_ifo->fps;
  out_codec_ctx->pix_fmt = AV_PIX_FMT_NV12;
  // out_codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
  out_codec_ctx->framerate = {pusher_ifo->fps, 1};
  out_codec_ctx->time_base = {1, pusher_ifo->fps};
  out_codec_ctx->bit_rate = pusher_ifo->bitrate * 1000;
  if (pfmt_ctx->oformat->flags & AVFMT_GLOBALHEADER) {
    out_codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
  }
  out_codec_ctx->extradata = sps_header.data;
  out_codec_ctx->extradata_size = sps_header.size;

  out_codec_ctx->thread_count = 3;
  out_codec_ctx->max_b_frames = 0;  //编码器不使用Ｂ帧，提高直播解码效率


  if ((ret = avcodec_parameters_from_context(out_stream->codecpar, out_codec_ctx)) < 0) {
    spdlog::get("logger")->info("ERROR. RKStreamPusher::init_params. Could not initialize stream codec parameters!");
    return -100;
  }

  
  AVDictionary *codec_options = nullptr;
  av_dict_set(&codec_options, "profile", pusher_ifo->codecProfile.c_str(), 0);
  av_dict_set(&codec_options, "preset", "superfast", 0);
  av_dict_set(&codec_options, "tune", "zerolatency", 0);
  // //无需打开编码器
  // open video encoder
  if ((ret = avcodec_open2(out_codec_ctx, out_codec, &codec_options)) < 0) {
    spdlog::get("logger")->info("ERROR. RKStreamPusher::init_params. Could not open video encoder!");
    return -104;
  }
  av_dict_free(&codec_options);


  out_stream->codecpar->extradata = out_codec_ctx->extradata;
  out_stream->codecpar->extradata_size = out_codec_ctx->extradata_size;

  out_stream->codecpar->codec_tag = 0;
  av_dump_format(pfmt_ctx, 0, pusher_ifo->streamAddress.c_str(), 1);

  if ((ret = avformat_write_header(pfmt_ctx, nullptr)) < 0) {
    spdlog::get("logger")->info("ERROR. RKStreamPusher::init_params. Could not write header!");
    return -105;
  }
  
  return 0;
}

int RKStreamPusher::add_frame(stream::ImageBlob& buffer) {
  int ret = 0;
  queue_lock.lock();
  if (queue_data.size() > 1) { 
    queue_data.pop(); 
    // printf("RKStreamPusher %d pop.\n", pusher_ifo->vEncChn);
  }
  queue_data.push(buffer);
  // std::cout << "RKStreamPusher::add_frame queue_data: " << queue_data.size() << std::endl;
  queue_lock.unlock();
  return 0;
}


int RKStreamPusher::init_venc_params() {
  memset(&args_, 0, sizeof(MpiEncArgs));

	//设置的输入帧的格式信息 yuv 420sp  即 NV12 yyyy...uv... 结构
	// args_.format = MPP_FMT_YUV420SP;
	// args_.format = MPP_FMT_YUV420P;
	args_.format = MPP_FMT_BGR888;

	//设置264编码格式， AVC     
	args_.type = (MppCodingType)encode_type;
	args_.fps_in_num =  pusher_ifo->fps;
  args_.fps_out_num = pusher_ifo->fps;
	args_.width = pusher_ifo->dstWidth;
	args_.height = pusher_ifo->dstHeight;
	args_.hor_stride = mpi_enc_width_default_stride(args_.width, args_.format);
	args_.ver_stride = args_.height;

  args_.bps_target = pusher_ifo->bitrate * 1000;  //码率

	args_.fps_in_flex = 0;   // 1- 帧率可变 0-固定
	args_.fps_in_num = pusher_ifo->fps;
	args_.fps_in_den = 1;

	args_.fps_out_flex = 0;  // 1- 帧率可变 0-固定
	args_.fps_out_num = pusher_ifo->fps;
	args_.fps_out_den = 1;
	// args_.qp_init = 28; //量化参数   //之前设置28

  MPP_RET ret = MPP_OK; 

	MppPollType timeout = MPP_POLL_BLOCK;

	printf("mpi_enc_test start\n");

	ret = enc_ctx_init(&p, &args_);
	if (ret) {
		printf("test data init failed ret %d\n", ret);
		deinit();
	}
	ret = mpp_buffer_group_get_internal(&p->buf_grp, MPP_BUFFER_TYPE_DRM);
    if (ret) {
        printf("failed to get mpp buffer group ret %d\n", ret);
        deinit();
    }

    ret = mpp_buffer_get(p->buf_grp, &p->frm_buf, p->frame_size + p->header_size);
	if (ret) {
		printf("failed to get buffer for input frame ret %d\n", ret);
		deinit();
	}

    ret = mpp_buffer_get(p->buf_grp, &p->pkt_buf, p->frame_size);
	if (ret) {
		printf("failed to get buffer for input osd index ret %d\n", ret);
		deinit();
	}

	printf("mpi_enc_test encoder test start w %d h %d type %d\n", p->width,
				 p->height, p->type);

	// encoder demo
	ret = mpp_create(&p->ctx, &p->mpi);
	if (ret) {
		printf("mpp_create failed ret %d\n", ret);
		deinit();
	}

	ret = p->mpi->control(p->ctx, MPP_SET_OUTPUT_TIMEOUT, &timeout);
	if (MPP_OK != ret) {
		printf("mpi control set output timeout %d ret %d\n", timeout, ret);
		deinit();
	}

	ret = mpp_init(p->ctx, MPP_CTX_ENC, p->type);
	if (ret) {
		printf("mpp_init failed ret %d\n", ret);
		deinit();
	}

	ret = mpp_enc_cfg_init(&p->cfg);
	if (ret) {
		printf("mpp_enc_cfg_init failed ret %d\n", ret);
		deinit();
	}

	ret = mpp_enc_cfg_setup_t(p);
	if (ret) {
		printf("test mpp setup failed ret %d\n", ret);
		deinit();
	}
  
  //get_header 将其设置在push_params
  uint8_t* enc_buf = sps_header.data;
  ret = GetHeader(&sps_header);
  if(ret){
    printf("GetHeader failed ret %d\n", ret);
		deinit();
  }

  return 0;
}

MPP_RET RKStreamPusher::enc_ctx_init(MpiEncData **data, MpiEncArgs *cmd) {
	MpiEncData *p = NULL;
	MPP_RET ret = MPP_OK;

	if (!data || !cmd) {
		printf("invalid input data %p cmd %p\n", data, cmd);
		return MPP_ERR_NULL_PTR;
	}

	p = mpp_calloc(MpiEncData, 1);
	if (!p) {
		printf("create MpiEncTestData failed\n");
		ret = MPP_ERR_MALLOC;
		*data = p;
		return ret;
	}

	// get paramter from cmd
    p->width        = cmd->width;
    p->height       = cmd->height;
    p->hor_stride   = (cmd->hor_stride) ? (cmd->hor_stride) :
                      (MPP_ALIGN(cmd->width, 16));
    p->ver_stride   = (cmd->ver_stride) ? (cmd->ver_stride) :
                      (MPP_ALIGN(cmd->height, 16));
    p->fmt          = cmd->format;
    p->type         = cmd->type;
    p->bps          = cmd->bps_target;
    p->bps_min      = cmd->bps_min;
    p->bps_max      = cmd->bps_max;
    p->rc_mode      = cmd->rc_mode;
    p->num_frames   = cmd->num_frames;
    if (cmd->type == MPP_VIDEO_CodingMJPEG && p->num_frames == 0) {
        printf("jpege default encode only one frame. Use -n [num] for rc case\n");
        p->num_frames   = 1;
    }
    p->gop_mode     = cmd->gop_mode;
    p->gop_len      = cmd->gop_len;
    p->vi_len       = cmd->vi_len;

    p->fps_in_flex  = cmd->fps_in_flex;
    p->fps_in_den   = cmd->fps_in_den;
    p->fps_in_num   = cmd->fps_in_num;
    p->fps_out_flex = cmd->fps_out_flex;
    p->fps_out_den  = cmd->fps_out_den;
    p->fps_out_num  = cmd->fps_out_num;
    p->qp_init =  cmd->qp_init;
	// update resource parameter
    switch (p->fmt & MPP_FRAME_FMT_MASK) {
    case MPP_FMT_YUV420SP:
    case MPP_FMT_YUV420P: {
        p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64) * 3 / 2;
    } break;

    case MPP_FMT_YUV422_YUYV :
    case MPP_FMT_YUV422_YVYU :
    case MPP_FMT_YUV422_UYVY :
    case MPP_FMT_YUV422_VYUY :
    case MPP_FMT_YUV422P :
    case MPP_FMT_YUV422SP : {
        p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64) * 2;
    } break;

    case MPP_FMT_RGB444 :
    case MPP_FMT_BGR444 :
    case MPP_FMT_RGB555 :
    case MPP_FMT_BGR555 :
    case MPP_FMT_RGB565 :
    case MPP_FMT_BGR565 : 
    case MPP_FMT_RGB888 :
    case MPP_FMT_BGR888 :
    case MPP_FMT_RGB101010 :
    case MPP_FMT_BGR101010 :
    case MPP_FMT_ARGB8888 :
    case MPP_FMT_ABGR8888 :
    case MPP_FMT_BGRA8888 :
    case MPP_FMT_RGBA8888 :{
        p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64);
    } break;

    default: {
        p->frame_size = MPP_ALIGN(p->hor_stride, 64) * MPP_ALIGN(p->ver_stride, 64) * 4;
    } break;
    }

    if (MPP_FRAME_FMT_IS_FBC(p->fmt))
        p->header_size = MPP_ALIGN(MPP_ALIGN(p->width, 16) * MPP_ALIGN(p->height, 16) / 16, SZ_4K);
    else
        p->header_size = 0;

	// /*
	//  * osd idx size range from 16x16 bytes(pixels) to
	//  * hor_stride*ver_stride(bytes). for general use, 1/8 Y buffer is enough.
	//  */
	// p->osd_idx_size = p->hor_stride * p->ver_stride / 8;
	// p->plt_table[0] = MPP_ENC_OSD_PLT_RED;
	// p->plt_table[1] = MPP_ENC_OSD_PLT_YELLOW;
	// p->plt_table[2] = MPP_ENC_OSD_PLT_BLUE;
	// p->plt_table[3] = MPP_ENC_OSD_PLT_GREEN;
	// p->plt_table[4] = MPP_ENC_OSD_PLT_CYAN;
	// p->plt_table[5] = MPP_ENC_OSD_PLT_TRANS;
	// p->plt_table[6] = MPP_ENC_OSD_PLT_BLACK;
	// p->plt_table[7] = MPP_ENC_OSD_PLT_WHITE;

	*data = p;
	return ret;
}


int RKStreamPusher::start_push_listener() {
  int ret = start_push_imp();
  spdlog::get("logger")->info("RKStreamPusher::start_push_listener start_push_imp ret:{}", ret);
  return ret;
}
int RKStreamPusher::start_push_imp() {
  MppApi *mpi = p->mpi;
  MppCtx ctx = p->ctx;
  MPP_RET ret = MPP_OK;
  MppFrame frame = NULL;
	MppMeta meta = NULL;
	RK_U32 eoi = 1;

  int pts = 0;
  float venc_fps;
  int pts_count = 0, fps_cnt = 0;
  int64_t start_time = 0, end_time = 0;
  std::vector<char> inBuffer;
  stream::ImageBlob buffer(stream::ImageBlobMode_BGR);
  int for_sleep = 3;
  stop_flag2 = common::CameraGrabing;
  while (stop_flag2 == common::CameraGrabing) {
    std::this_thread::sleep_for(std::chrono::milliseconds(for_sleep));
    if (fps_cnt == 0) { start_time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();}
    
    queue_lock.lock();
    if (queue_data.empty()) { queue_lock.unlock(); continue; }
    buffer = queue_data.front();
    queue_data.pop();
    queue_lock.unlock();
    if(buffer.img.empty()){continue;}

    cv::Mat bgr_img;
    if (buffer.img.channels() == 1) { cv::cvtColor(buffer.img, bgr_img, cv::COLOR_GRAY2BGR);} 
    else { bgr_img = buffer.img; }
    if (bgr_img.cols != pusher_ifo->dstWidth || bgr_img.rows != pusher_ifo->dstHeight) {
      cv::resize(bgr_img, bgr_img, cv::Size(pusher_ifo->dstWidth, pusher_ifo->dstHeight));
    }

    fps_cnt++; 
    void *buf = mpp_buffer_get_ptr(p->frm_buf);
	  memcpy(buf, bgr_img.data, bgr_img.cols * bgr_img.rows * 3);
    ret = mpp_frame_init(&frame);
    if (ret) {
      spdlog::get("logger")->info("ERROR. RKStreamPusher::start_push_listener mpp_frame_init failed");
      break;
    }
    mpp_frame_set_width(frame, p->width);
    mpp_frame_set_height(frame, p->height);
    mpp_frame_set_hor_stride(frame, p->hor_stride);
    mpp_frame_set_ver_stride(frame, p->ver_stride);
    mpp_frame_set_fmt(frame, p->fmt);
    mpp_frame_set_eos(frame, p->frm_eos);
    mpp_frame_set_buffer(frame, p->frm_buf);
    // mpp_frame_set_pts(frame, pts);
    // meta = mpp_frame_get_meta(frame);

    // mpp_packet_init_with_buffer(&packet, p->pkt_buf);
    // /* NOTE: It is important to clear output packet length!! */
    // mpp_packet_set_length(packet, 0);
    // mpp_meta_set_packet(meta, KEY_OUTPUT_PACKET, packet);
  
    ret = mpi->encode_put_frame(ctx, frame);
    if (ret) {
      spdlog::get("logger")->info("ERROR. RKStreamPusher::start_push_listener mpp encode put frame failed");
      mpp_frame_deinit(&frame);
      continue;
    }
    encode_put_frame_flag = 1;
    // spdlog::get("logger")->info("start_push_listener. encode_put_frame_flag:{}", encode_put_frame_flag);

    mpp_frame_deinit(&frame);
    end_time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    if (pts % 300 == 0) { 
      printf("venc SendFrame %d, ret=%d, %lld\n", pts, ret, end_time); 
      spdlog::get("logger")->info("RKStreamPusher::start_push_listener venc SendFrame pts:{}, ret:{}, end_time:{}", pts, ret, end_time);
    }
    pts++;

    // 输出fps
    if (end_time - start_time > 5 * 1e3) {
      venc_fps = 1.0 / ((end_time - start_time) / (fps_cnt * 1e3));
      printf("venc_fps c:%d : %f\n", pusher_ifo->vEncChn, venc_fps);
      spdlog::get("logger")->info("RKStreamPusher::start_push_imp. c:{}, venc_fps:{}", pusher_ifo->vEncChn, venc_fps);
      fps_cnt = 0;
    }
  }

  stop_flag2 = common::CameraClosed;
  spdlog::get("logger")->info("ERROR. start_push_imp exit.\n");
  return 0;
}

int RKStreamPusher::init_params() {
  int ret = 0;

  if ((ret = init_pusher_params()) != 0) {
    printf("[ERROR] init_pusher_params %d error\n", pusher_ifo->vEncChn);
    return ret;
  }

  stop_flag = common::CameraOpened;
  return 0;
}

int RKStreamPusher::start_push() {
  int ret = 0;
  MppApi *mpi = p->mpi;
  MppCtx ctx = p->ctx;

  // 初始化完成
  std::thread(&RKStreamPusher::start_push_listener, this).detach();

  int frame_cnt = 0;
  int64_t pts = 0;
  float push_fps;
  int pts_count = 0, fps_cnt = 0;
  int64_t start_time = 0, end_time = 0;
  int64_t prev_time, cur_time;
  prev_time = cur_time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();;
  int step_time = 1000 / pusher_ifo->fps;
  int send_ok = 0;

  MppPacket packet;
  stop_flag = common::CameraGrabing;
  while (stop_flag == common::CameraGrabing) {
    cur_time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    if (fps_cnt == 0) { start_time = cur_time; }

    // 控制发送速度
    if (send_ok >= 0) {
      int leave_time = step_time - (cur_time - prev_time);
      if (leave_time > 0) std::this_thread::sleep_for(std::chrono::milliseconds(leave_time));
      prev_time = cur_time + leave_time;
    } else {
      std::this_thread::sleep_for(std::chrono::milliseconds(2));
    }
    // 增加标志判断, encode_put_frame 放一个，encode_get_packet 才能拿一个, 如果 encode_put_frame 不放, 放一个，encode_get_packet 就会永远阻塞
    if (encode_put_frame_flag) {
      // spdlog::get("logger")->info("start_push. encode_put_frame_flag:{}, packet:{}", encode_put_frame_flag, packet == nullptr);
      ret = mpi->encode_get_packet(ctx, &packet);
      encode_put_frame_flag = 0;
      // spdlog::get("logger")->info("start_push. encode_put_frame_flag:{}, ret:{}, packet:{}", encode_put_frame_flag, ret, packet == nullptr);
      if (ret) {printf("encode_get_packet error! ret : %d\n",ret); send_ok = -1; continue;}
    }
    // printf("venc GetStream %d ret=%d\n", pts, ret);

    if (packet) {
      void *ptr =  mpp_packet_get_pos(packet);
      size_t ptr_len  = mpp_packet_get_length(packet);
      fps_cnt++;
      AVPacket *encode_pkt = av_packet_alloc();
      ret = av_new_packet(encode_pkt, ptr_len);
      memcpy(encode_pkt->data, ptr, ptr_len);
      
      pts += av_rescale_q(1, out_codec_ctx->time_base, out_stream->time_base);
      encode_pkt->pts = pts;
      av_interleaved_write_frame(pfmt_ctx, encode_pkt);
      av_packet_unref(encode_pkt);
      av_packet_free(&encode_pkt);
      send_ok = 0;
      
      mpp_packet_deinit(&packet); 
      p->stream_size += ptr_len;
      p->frame_count++;
    }
    else {send_ok = -1; continue;}
    // std::cout<<"fps_cnt : "<<fps_cnt<<std::endl;

    // 输出fps
    end_time = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
    if (end_time - start_time > 5 * 1e3) {
      push_fps = 1.0 / ((end_time - start_time) / (fps_cnt * 1e3));
      // spdlog::get("logger")->info("pull_fps: {}", pull_fps);
      printf("push_fps %d: %f\n", pusher_ifo->vEncChn, push_fps);
      fps_cnt = 0;
    }
  }
  spdlog::get("logger")->info("ERROR. start_push exit.\n");
  release_ffmpeg();
  deinit();
  stop_flag = common::CameraClosed;
  return 0;
}


MPP_RET RKStreamPusher::mpp_enc_cfg_setup_t(MpiEncData *p) {
	MPP_RET ret;
	MppApi *mpi;
	MppCtx ctx;
	MppEncCfg cfg;	
	MppEncRcMode rc_mode = MPP_ENC_RC_MODE_AVBR;

	if (NULL == p) return MPP_ERR_NULL_PTR;

	mpi = p->mpi;
	ctx = p->ctx;
	cfg = p->cfg;

	/* setup default parameter */
	if (p->fps_in_den == 0) p->fps_in_den = 1;
	if (p->fps_in_num == 0) p->fps_in_num = 30;
	if (p->fps_out_den == 0) p->fps_out_den = 1;
	if (p->fps_out_num == 0) p->fps_out_num = 30;
	// p->gop = 60;

	if (!p->bps)
		p->bps = p->width * p->height / 8 * (p->fps_out_num / p->fps_out_den);

	mpp_enc_cfg_set_s32(cfg, "prep:width", p->width);
	mpp_enc_cfg_set_s32(cfg, "prep:height", p->height);
	mpp_enc_cfg_set_s32(cfg, "prep:hor_stride", p->hor_stride);
	mpp_enc_cfg_set_s32(cfg, "prep:ver_stride", p->ver_stride);
	mpp_enc_cfg_set_s32(cfg, "prep:format", p->fmt);

	mpp_enc_cfg_set_s32(cfg, "rc:mode", rc_mode);

	/* fix input / output frame rate */
	mpp_enc_cfg_set_s32(cfg, "rc:fps_in_flex", p->fps_in_flex);
	mpp_enc_cfg_set_s32(cfg, "rc:fps_in_num", p->fps_in_num);
	mpp_enc_cfg_set_s32(cfg, "rc:fps_in_denorm", p->fps_in_den);
	mpp_enc_cfg_set_s32(cfg, "rc:fps_out_flex", p->fps_out_flex);
	mpp_enc_cfg_set_s32(cfg, "rc:fps_out_num", p->fps_out_num);
	mpp_enc_cfg_set_s32(cfg, "rc:fps_out_denorm", p->fps_out_den);
    // mpp_enc_cfg_set_s32(cfg, "rc:gop", p->gop_len ? p->gop_len : p->fps_out_num * 2);
    mpp_enc_cfg_set_s32(cfg, "rc:gop", p->gop_len ? p->gop_len : p->fps_out_num);  //25

	    /* drop frame or not when bitrate overflow */
    mpp_enc_cfg_set_u32(cfg, "rc:drop_mode", MPP_ENC_RC_DROP_FRM_DISABLED);
    mpp_enc_cfg_set_u32(cfg, "rc:drop_thd", 20);        /* 20% of max bps */
    mpp_enc_cfg_set_u32(cfg, "rc:drop_gap", 1);         /* Do not continuous drop frame */

    /* setup bitrate for different rc_mode */
    mpp_enc_cfg_set_s32(cfg, "rc:bps_target", p->bps);
    switch (p->rc_mode) {
    case MPP_ENC_RC_MODE_FIXQP : {
        printf("FIXQp\n");
        /* do not setup bitrate on FIXQP mode */
    } break;
    case MPP_ENC_RC_MODE_CBR : {
        printf("CBR\n");
        /* CBR mode has narrow bound */
        mpp_enc_cfg_set_s32(cfg, "rc:bps_max", p->bps_max ? p->bps_max : p->bps * 17 / 16);
        mpp_enc_cfg_set_s32(cfg, "rc:bps_min", p->bps_min ? p->bps_min : p->bps * 15 / 16);
    } break;
    case MPP_ENC_RC_MODE_VBR :
    case MPP_ENC_RC_MODE_AVBR : {
        printf("AVBR \n");
        /* VBR mode has wide bound */
        mpp_enc_cfg_set_s32(cfg, "rc:bps_max", p->bps_max ? p->bps_max : p->bps * 17 / 16);
        mpp_enc_cfg_set_s32(cfg, "rc:bps_min", p->bps_min ? p->bps_min : p->bps * 1 / 16);
    } break;
    default : {
        /* default use CBR mode */
        printf("default");
        mpp_enc_cfg_set_s32(cfg, "rc:bps_max", p->bps_max ? p->bps_max : p->bps * 17 / 16);
        mpp_enc_cfg_set_s32(cfg, "rc:bps_min", p->bps_min ? p->bps_min : p->bps * 15 / 16);
    } break;
    }

    /* setup qp for different codec and rc_mode */
    switch (p->type) {
    case MPP_VIDEO_CodingAVC :
    case MPP_VIDEO_CodingHEVC : {
        switch (p->rc_mode) {
        case MPP_ENC_RC_MODE_FIXQP : {
           RK_S32 fix_qp = p->qp_init;
            mpp_enc_cfg_set_s32(cfg, "rc:qp_init", fix_qp);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_max", fix_qp);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_min", fix_qp);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", fix_qp);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", fix_qp);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 0);
        } break;
        case MPP_ENC_RC_MODE_CBR :
        case MPP_ENC_RC_MODE_VBR :
        case MPP_ENC_RC_MODE_AVBR : {
            mpp_enc_cfg_set_s32(cfg, "rc:qp_init", -1);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_max", 51);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_min", 10);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", 51);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", 10);
            mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 2);
            mpp_enc_cfg_set_s32(cfg, "rc:fqp_min_i", 10);
            mpp_enc_cfg_set_s32(cfg, "rc:fqp_max_i", 51);
            mpp_enc_cfg_set_s32(cfg, "rc:fqp_min_p", 10);
            mpp_enc_cfg_set_s32(cfg, "rc:fqp_max_p", 51);
        } break;
        default : {
            printf("unsupport encoder rc mode %d\n", p->rc_mode);
        } break;
        }
    } break;
    case MPP_VIDEO_CodingVP8 : {
        /* vp8 only setup base qp range */
        mpp_enc_cfg_set_s32(cfg, "rc:qp_init", 40);
        mpp_enc_cfg_set_s32(cfg, "rc:qp_max",  127);
        mpp_enc_cfg_set_s32(cfg, "rc:qp_min",  0);
        mpp_enc_cfg_set_s32(cfg, "rc:qp_max_i", 127);
        mpp_enc_cfg_set_s32(cfg, "rc:qp_min_i", 0);
        mpp_enc_cfg_set_s32(cfg, "rc:qp_ip", 6);
    } break;
    case MPP_VIDEO_CodingMJPEG : {
        /* jpeg use special codec config to control qtable */
        mpp_enc_cfg_set_s32(cfg, "jpeg:q_factor", 80);
        mpp_enc_cfg_set_s32(cfg, "jpeg:qf_max", 99);
        mpp_enc_cfg_set_s32(cfg, "jpeg:qf_min", 1);
    } break;
    default : {
    } break;
    }

    /* setup codec  */
    mpp_enc_cfg_set_s32(cfg, "codec:type", p->type);
    switch (p->type) {
    case MPP_VIDEO_CodingAVC : {
        RK_U32 constraint_set;

        /*
         * H.264 profile_idc parameter
         * 66  - Baseline profile
         * 77  - Main profile
         * 100 - High profile
         */
        mpp_enc_cfg_set_s32(cfg, "h264:profile", 100);
        /*
         * H.264 level_idc parameter
         * 10 / 11 / 12 / 13    - qcif@15fps / cif@7.5fps / cif@15fps / cif@30fps
         * 20 / 21 / 22         - cif@30fps / half-D1@@25fps / D1@12.5fps
         * 30 / 31 / 32         - D1@25fps / 720p@30fps / 720p@60fps
         * 40 / 41 / 42         - 1080p@30fps / 1080p@30fps / 1080p@60fps
         * 50 / 51 / 52         - 4K@30fps
         */
        mpp_enc_cfg_set_s32(cfg, "h264:level", 40);
        mpp_enc_cfg_set_s32(cfg, "h264:cabac_en", 1);
        mpp_enc_cfg_set_s32(cfg, "h264:cabac_idc", 0);
        mpp_enc_cfg_set_s32(cfg, "h264:trans8x8", 1);
        
        mpp_env_get_u32("constraint_set", &constraint_set, 0);
        if (constraint_set & 0x3f0000)
        mpp_enc_cfg_set_s32(cfg, "h264:constraint_set", constraint_set);
    } break;
    case MPP_VIDEO_CodingHEVC :
    case MPP_VIDEO_CodingMJPEG :
    case MPP_VIDEO_CodingVP8 : {
    } break;
    default : {
        printf("unsupport encoder coding type %d\n", p->type);
    } break;
    }

    p->split_mode = 0;
    p->split_arg = 0;

    mpp_env_get_u32("split_mode", &p->split_mode, MPP_ENC_SPLIT_NONE);
    mpp_env_get_u32("split_arg", &p->split_arg, 0);

    if (p->split_mode) {
        printf("%p split_mode %d split_arg %d\n", ctx, p->split_mode, p->split_arg);
        mpp_enc_cfg_set_s32(cfg, "split:mode", p->split_mode);
        mpp_enc_cfg_set_s32(cfg, "split:arg", p->split_arg);
    }

    ret = mpi->control(ctx, MPP_ENC_SET_CFG, cfg);
    if (ret) {
        printf("mpi control enc set cfg failed ret %d\n", ret);
        return ret;
    }

    /* optional */
    p->sei_mode = MPP_ENC_SEI_MODE_ONE_FRAME;
    ret = mpi->control(ctx, MPP_ENC_SET_SEI_CFG, &p->sei_mode);
    if (ret) {
        printf("mpi control enc set sei cfg failed ret %d\n", ret);
        return ret;
    }

    if (p->type == MPP_VIDEO_CodingAVC || p->type == MPP_VIDEO_CodingHEVC) {
        p->header_mode = MPP_ENC_HEADER_MODE_EACH_IDR;
        ret = mpi->control(ctx, MPP_ENC_SET_HEADER_MODE, &p->header_mode);
        if (ret) {
            printf("mpi control enc set header mode failed ret %d\n", ret);
            return ret;
        }
    }

    RK_U32 gop_mode = p->gop_mode;

    mpp_env_get_u32("gop_mode", &gop_mode, gop_mode);
    if (gop_mode) {
        MppEncRefCfg ref;

        mpp_enc_ref_cfg_init(&ref);

        if (p->gop_mode < 4)
            mpi_enc_gen_ref_cfg(ref, gop_mode);
        else
            mpi_enc_gen_smart_gop_ref_cfg(ref, p->gop_len, p->vi_len);

        ret = mpi->control(ctx, MPP_ENC_SET_REF_CFG, ref);
        if (ret) {
            printf("mpi control enc set ref cfg failed ret %d\n", ret);
            return ret;
        }
        mpp_enc_ref_cfg_deinit(&ref);
    }

    /* setup test mode by env */
    mpp_env_get_u32("osd_enable", &p->osd_enable, 0);
    mpp_env_get_u32("osd_mode", &p->osd_mode, MPP_ENC_OSD_PLT_TYPE_DEFAULT);
    mpp_env_get_u32("roi_enable", &p->roi_enable, 0);
    mpp_env_get_u32("user_data_enable", &p->user_data_enable, 0);

	// RET:
	return ret;
}

MPP_RET RKStreamPusher::GetHeader(SpsHeader *sps_header) {
    MPP_RET ret = MPP_OK; 
    // void* out_ptr = enc_buf;
    // size_t out_len = 0;

    if (pusher_ifo->enType == AV_CODEC_ID_H264 || pusher_ifo->enType == AV_CODEC_ID_H265) {
        MppPacket packet = NULL;

        /*
         * Can use packet with normal malloc buffer as input not pkt_buf.
         * Please refer to vpu_api_legacy.cpp for normal buffer case.
         * Using pkt_buf buffer here is just for simplifing demo.
         */
        mpp_packet_init_with_buffer(&packet, p->pkt_buf);
        /* NOTE: It is important to clear output packet length!! */
        mpp_packet_set_length(packet, 0);

        ret = p->mpi->control(p->ctx, MPP_ENC_GET_HDR_SYNC, packet);
        if (ret) {
            printf("mpi control enc get extra info failed\n");
            return MPP_NOK;
        } else {
            /* get and write sps/pps for H.264 */

            void *ptr   = mpp_packet_get_pos(packet);
            size_t len  = mpp_packet_get_length(packet);
            sps_header->data = (uint8_t*)malloc(len);
            sps_header->size = len;
            memcpy(sps_header->data,ptr,len);
            // memcpy(out_ptr, ptr, len);
            // out_ptr = (uint8_t*)(out_ptr) + len;
            // out_len += len;
        }

        mpp_packet_deinit(&packet);
    }
    return MPP_OK;
}

int RKStreamPusher::stop_push() {
  int try_cnt = 200;
  if (stop_flag2 == common::CameraGrabing) {
    stop_flag2 = common::CameraOpened;
    while (stop_flag2 != common::CameraClosed && try_cnt-- > 0) {
      spdlog::get("logger")->info("RKStreamPusher::stop_push stop_flag2: {} ", stop_flag2);
      std::this_thread::sleep_for(milliseconds(50));
    }
    if (try_cnt < 0) {exit(0);}
  }

  try_cnt = 200;
  if (stop_flag == common::CameraGrabing) {
    stop_flag = common::CameraOpened;
    while (stop_flag != common::CameraClosed && try_cnt-- > 0) {
      spdlog::get("logger")->info("RKStreamPusher::stop_push stop_flag: {} ", stop_flag);
      std::this_thread::sleep_for(milliseconds(50));
    }
    if (try_cnt < 0) {exit(0);}
  }

  return 0;
}


}